package com.esdk.utils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import com.esdk.esdk;

import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateNotFoundException;

public class ModulesCodeGenerator{
	String[] excludeFieldNames= {"login_center_id","password","create_user_id","create_user_name","modify_user_id","modify_user_name","delete_user_id","delete_user_name","delete_time","upload_user_name","version","valid","upload_user_id","upload_user_name"};
	private boolean isGenerateController=true,isGenerateService=false,isGenerateUploadService=false,isGenerateVuePage=true;
	private String webrootPath=esdk.obj.or(esdk.regex.findSub(esdk.prop.getString("spring.resources.static-locations","./webroot/"),".*?file:(.*?),.*",1),"./src/main/resources/static");
	private String templatePath;
	private String ormPackageName=esdk.prop.getString("esdk.orm.packageName","com.esdk.orm");
	private String prefixPackage;
	private String srcPrefixPath;
	
	public ModulesCodeGenerator(){
		this.setTemplatePath("src/test/resources/template");
		this.prefixPackage=ormPackageName.replace(".orm","");
		this.srcPrefixPath="./src/main/java/"+prefixPackage.replace('.','/').replace('.','/');
		this.setGenerateController(true);
		this.setGenerateService(true);
		this.setGenerateUploadService(false);
		this.setGenerateVuePage(true);
		this.webrootPath="./ruoyi";
	}
	
	public ModulesCodeGenerator(String ormPackageName,String pkgPrefixPath,String srcPrefixPath){
		this();
		this.ormPackageName=esdk.obj.or(ormPackageName,this.ormPackageName);
		this.prefixPackage=esdk.obj.or(pkgPrefixPath,this.prefixPackage);
		this.srcPrefixPath=esdk.obj.or(srcPrefixPath,this.srcPrefixPath);
	}
	
	
	public void generateFiles(String...tableNames) throws Exception{
		for(String tablename:tableNames) {
			this.process(tablename);
		}
		generateServiceFactory();

	}

	private void generateServiceFactory() throws TemplateNotFoundException,MalformedTemplateNameException,ParseException,IOException,TemplateException{
		Configuration cfg=new Configuration(Configuration.VERSION_2_3_22);
		cfg.setDirectoryForTemplateLoading(new File(getTemplatePath()));
		HashMap params=new HashMap();
		params.put("packageRootPath",prefixPackage);
		params.put("ormPackageName",ormPackageName);
		
		//生成DF.java
		Template tdf=cfg.getTemplate("DF.java.ftl");
		File daoFactoryJavaFile=new File(srcPrefixPath+"/DF.java");
		File[] daoFiles=esdk.file.findFiles(new File(srcPrefixPath),".+?Row.java");
		List<Map> daoList=new ArrayList<Map>();
		for(File rowFile:daoFiles) {
			Map item=new HashMap(2);
			String modelName=rowFile.getName().replaceAll("Row.java","");
			item.put("ModelName",modelName);
			String className=modelName+"MetaData";
			String tableComment=(String)esdk.reflect.getFieldValue(esdk.reflect.safeNewInstance(ormPackageName+'.'+className),"TableComment");
			item.put("tableComment",tableComment);
			daoList.add(item);
		}
		params.put("daoList",daoList);
		tdf.process(params,new FileWriter(daoFactoryJavaFile,false));
		System.out.println("已生成文件:"+daoFactoryJavaFile.getCanonicalPath());
		
		if(isGenerateService) {
			//生成SF.java
			Template tsf=cfg.getTemplate("SF.java.ftl");
			File serviceFactoryJavaFile=new File(srcPrefixPath+"/service/SF.java");
			File[] serviceFiles=esdk.file.findFiles(new File(srcPrefixPath+"/service/"),"Service.java");
			List<String> serviceList=new ArrayList<String>();
			for(File serviceFile:serviceFiles) {
				serviceList.add(serviceFile.getName().replaceAll(".java",""));
			}
			params.put("serviceList",serviceList);
			tsf.process(params,new FileWriter(serviceFactoryJavaFile,false));
			System.out.println("已生成文件:"+serviceFactoryJavaFile.getCanonicalPath());
		}
	}
	
	public void setGenerateController(boolean b){
		isGenerateController=b;
	}

	public String getWebRootPath(){
		return webrootPath;
	}
	
	public void process(String tableName) throws Exception{
		String modelName=EasyStr.toCamelCase(tableName,true);
		Configuration cfg=new Configuration(Configuration.VERSION_2_3_22);
		cfg.setDirectoryForTemplateLoading(new File(getTemplatePath()));
		HashMap params=new HashMap();
		String metadataClassPath=ormPackageName+"."+modelName+"MetaData";
		Object tableMetadata=esdk.reflect.safeNewInstance(esdk.reflect.findClass(metadataClassPath));
		String metadataViewClassPath=ormPackageName+"."+modelName+"View"+"MetaData";
		Object viewMetadata=esdk.reflect.safeNewInstance(esdk.reflect.findClass(metadataViewClassPath,metadataClassPath));
		params.put("ModelName",modelName);
		params.put("tableName",esdk.str.lowerFirst(tableName));
		String lowerFirstModelName=esdk.str.lowerFirst(esdk.regex.findSub(modelName,"(sys)?(.*)$",2));
		params.put("modelName",lowerFirstModelName);
		String requestMappingPath=getRequestMapping(modelName);
		params.put("requestMapping",requestMappingPath);

		params.put("packageRootPath",prefixPackage);
		params.put("ormPackageName",ormPackageName);
		params.put("ViewName",viewMetadata.getClass().getSimpleName().replace("MetaData",""));
		params.put("outputOverrideMethod",true);
		params.put("isGenerateService",isGenerateService);
		params.put("isGenerateUploadService",isGenerateUploadService);
		params.put("tableComment",getTableComment(tableMetadata));
//		params.put("fieldList",getFieldList(viewMetadata,tableMetadata));
		
		if(isGenerateController||isGenerateVuePage) {
			params.put("searchFieldList",getSearchFieldList(tableMetadata));
			params.put("searchLikeFieldList",new EasyQuery((List)params.get("searchFieldList")).filter("classType=String").toList());
			params.put("formFieldList",getFormFieldList(viewMetadata,tableMetadata));
		}

		if(isGenerateController) {		//生成XxxxController.java
//			cfg.setSharedVariable("max",new Max());
			Template t=cfg.getTemplate("controller.java.ftl");
			File controllerJavaFile=new File(srcPrefixPath+"/controller/"+modelName+"Controller.java");
			FileWriter out=new FileWriter(controllerJavaFile,false);
			t.process(params,out);
			System.out.println("服务器端Controller.java文件已生成:"+controllerJavaFile.getCanonicalPath());
		}
		if(isGenerateService) {		//生成XxxxController.java
			Template t=cfg.getTemplate("service.java.ftl");
			File serviceJavaFile=new File(srcPrefixPath+"/service/"+modelName+"Service.java");
			FileWriter out=new FileWriter(serviceJavaFile,false);
			t.process(params,out);
			System.out.println("服务器端Service.java文件已生成:"+serviceJavaFile.getCanonicalPath());
		}
		if(isGenerateUploadService) {
			Template tu=cfg.getTemplate("upload_service.java.ftl");
			File uploadServiceJavaFile=new File(srcPrefixPath+"/service/"+modelName+"UploadService.java");
			tu.process(params,new FileWriter(uploadServiceJavaFile,false));
			System.out.println("服务器端UploadService.java文件已生成:"+uploadServiceJavaFile.getCanonicalPath());
		}
		
		params.put("contextPath","${contextPath}");
		params.put("dollar","$");
		params.put("lang","$");
		if(isGenerateVuePage) {
			params.put("searchTimeFieldList",getSearchTimeFieldList(viewMetadata));
			params.put("tableFieldList",getTableFieldList(viewMetadata,tableMetadata));
			params.put("primaryKeyName",EasyStr.toCamelCase((String)EasyReflect.getFieldValue(viewMetadata,"PrimaryKey")));
			String defaultSortField=esdk.reflect.hasPropertyName(tableMetadata.getClass(),"ModifyTime")?"ModifyTime":"PrimaryKey";
			params.put("sortName", esdk.str.toCamelCase((String)esdk.reflect.getDeclaredFieldValue(viewMetadata,defaultSortField)));
			params.put("namespace",EasyStr.toCamelCase(tableName));
			//生成列表页面
			params.put("tableOperationButtonType","dropdown"); //下拉菜单式按钮风格
			params.put("useDefaultSort",false);
			File modelFtlFile=new File(getWebRootPath()+"/src/views/"+requestMappingPath+".vue");
			Template tv=cfg.getTemplate("model.vue.ftl");
			tv.process(params,new FileWriter(modelFtlFile,false));
			System.out.println("已生成前端VUE文件:"+modelFtlFile.getCanonicalPath());
			//生成js文件
			File modelJsFile=new File(getWebRootPath()+"/src/views/"+requestMappingPath+".js");
			Template tj=cfg.getTemplate("model.js.ftl");
			tj.process(params,new FileWriter(modelJsFile,false));
			System.out.println("已生成前端js文件:"+modelJsFile.getCanonicalPath());
			// 生成表单页面
			File modelFormFtlFile=new File(getWebRootPath()+"/src/views/"+requestMappingPath+"Form.vue");
			Template tf=cfg.getTemplate("modelForm.vue.ftl");
			tf.process(params,new FileWriter(modelFormFtlFile,false));
			System.out.println("已生成前端表单VUE文件:"+modelFormFtlFile.getCanonicalPath());
		}
		
		System.out.println("请配置【系统菜单】及【角色权限】");
	}

	private Properties requestMap;
	private String getRequestMapping(String fieldNameOrModelName){
		if(isGenerateController && requestMap==null) {
			requestMap=esdk.file.getProperties("requestMapping.properties");
			String result=requestMap.getProperty(fieldNameOrModelName,esdk.str.lowerFirst(fieldNameOrModelName));
			return result;
		}else
			return null;
	}

	private Object getTableComment(Object tableMetadata){
		String tableComment=EasyReflect.getDeclaredFieldValue(tableMetadata,"TableComment").toString();
		if(tableComment.lastIndexOf("表")==tableComment.length()-1)
			tableComment=tableComment.substring(0,tableComment.length()-1);
		return tableComment;
	}
	
	public List<Map> getFormFieldList(Object viewMetaData,Object tableMetaData) {
		List<Map> result=new ArrayList<Map>();
		String[] viewFields=(String[])EasyReflect.getDeclaredFieldValue(viewMetaData,"FieldNames");
		String[] tableFields=(String[])EasyReflect.getDeclaredFieldValue(tableMetaData,"FieldNames");
		String primaryKey=(String)EasyReflect.getDeclaredFieldValue(tableMetaData,"PrimaryKey");
		String[] excludesFields=esdk.array.concat(excludeFieldNames,new String[] {"create_time","modify_time","delete_time",primaryKey});
		for(Map<String,Object> item:getFieldList(viewMetaData,tableMetaData)) {
			String javaFieldName=(String)item.get("fieldName");
			if(javaFieldName.equals("centerId"))
				continue;
			if(!esdk.array.contains(excludesFields,esdk.str.toUnderlineCase(javaFieldName)))
				if(esdk.array.contains(tableFields,esdk.str.toUnderlineCase((String)item.get("fieldName"))))
					result.add(item);
		}
		return result;
	}
	
	public List<Map> getTableFieldList(Object viewMetaData,Object tableMetaData) {
		List<Map> result=new ArrayList<Map>();
		String excludesFields="deleteTime,valid";
		List<Map<String,Object>> fieldList=getFieldList(viewMetaData,tableMetaData);
		for(Map<String,Object> item:fieldList) {
			if(!excludesFields.contains((String)item.get("fieldName")) && !item.get("fieldName").toString().endsWith("Id")) {
				int width=esdk.math.max(item.get("label").toString().length()*27+3,70);
				if(item.get("fieldName").toString().endsWith("time"))
					width=150;//时间字段的长度至少要150px，否则无法显示完整时间。
				item.put("width",width);
				result.add(item);
			}
		}
		return result;
	}
	
	public List<Map<String,Object>> getFieldList(Object viewMetaData,Object tableMetaData) {
		ArrayList<Map<String,Object>> result=new ArrayList();
		String[] fields=(String[])EasyReflect.getDeclaredFieldValue(viewMetaData,"FieldNames");
		String[] tableFields=(String[])EasyReflect.getDeclaredFieldValue(tableMetaData,"FieldNames");
		Class[] fieldTypes=(Class[])EasyReflect.getDeclaredFieldValue(viewMetaData,"FieldTypes");
		String[] remarks=(String[])EasyReflect.getDeclaredFieldValue(viewMetaData,"Remarks");
		boolean[] allowNulls=(boolean[])EasyReflect.getDeclaredFieldValue(viewMetaData,"isNullables");
		String[] excludeFieldNames= {"create_user_name","modify_user_name","delete_user_name","delete_time","upload_user_name","version"};
		for(int i=0;i<fields.length;i++) {
			HashMap<String,Object> map=new HashMap();
			if(!esdk.array.contains(excludeFieldNames,fields[i])) {
				String javaFieldName=EasyStr.toCamelCase(fields[i],false);
				map.put("fieldName",javaFieldName);
				map.put("classType",fieldTypes[i].getSimpleName());
				map.put("required",String.valueOf(!allowNulls[i]));
				map.put("remark",remarks[i]);
				map.put("label",getFieldLabel(fields[i],remarks[i]));
				map.put("inputType",getInputType(javaFieldName,fieldTypes[i]));
				map.put("dropdownUrl",getDropdownUrl(javaFieldName,fieldTypes[i],fields));
				map.put("isViewColumn",!esdk.str.existOf(tableFields,fields[i]));
				result.add(map);
			}
		}
		return result;
	}
	
	private String getInputType(String javaFieldName,Class fieldType) {
		String result="";
		if(javaFieldName.endsWith("regionId"))
			result="cascader";
		else if("orgName,orgType,status,flow,sex".contains(javaFieldName)|| javaFieldName.endsWith("Id") || javaFieldName.matches(".*?(t|T)ype"))
			result="select";
		else if(fieldType.equals(Boolean.class))
			result="select";
		else if(fieldType.equals(Date.class)) 
			result=javaFieldName.endsWith("Date")?"date":"datetime";
		else if(javaFieldName.matches(".*?(c|C)ontent") && fieldType.equals(String.class))  //content结尾的默认为textarea，这里偷懒了，没有从数据库判断类型为text
			result="textarea";
		else 
			result="input";
		return result;
	}
		
	private String getDropdownUrl(String javaFieldName,Class fieldType,String[] fields) {
		String result="cannotFoundUrl";
		String category=null;
		if(javaFieldName.matches("sex"))
			category=esdk.str.toUnderlineCase(javaFieldName)+"_type";
		else 	if(javaFieldName.matches(".*?(t|T)ype")) {
			category=esdk.str.toUnderlineCase(javaFieldName);
		}else if(javaFieldName.endsWith("Id") && esdk.str.existOf(fields,esdk.str.toUnderlineCase(javaFieldName))) {
			result=esdk.str.format("/{}/dropdown.do",getRequestMapping(javaFieldName));
		}
		if(category!=null)
			result=esdk.str.format("/system/dict/dropdown.do?category={}",category);
		return result;
	}
	
	public List<Map> filterStringFieldList(List<Map> fieldList) {
		List<Map> result=new EasyQuery(fieldList).filter("classType=String").toList();
		return result;
	}
	
	private List<Map> getSearchFieldList(Object metadata) {
		ArrayList<Map> result=new ArrayList();
		String primaryKey=(String)EasyReflect.getDeclaredFieldValue(metadata,"PrimaryKey");
		String[] fields=(String[])EasyReflect.getDeclaredFieldValue(metadata,"FieldNames");
		Class[] fieldTypes=(Class[])EasyReflect.getDeclaredFieldValue(metadata,"FieldTypes");
		String[] remarks=(String[])EasyReflect.getDeclaredFieldValue(metadata,"Remarks");
		boolean[] allowNulls=(boolean[])EasyReflect.getDeclaredFieldValue(metadata,"isNullables");
		for(int i=0;i<fields.length;i++) {
			HashMap map=new HashMap();
			String javaFieldName=EasyStr.toCamelCase(fields[i],false);
			String classType=fieldTypes[i].getSimpleName();
			if(!EasyArray.contains(excludeFieldNames,fields[i]) && !fields[i].equals(primaryKey) && !hasIdField(fields[i],fields)) {
				map.put("fieldName",javaFieldName);
				map.put("classType",classType);
				map.put("required",String.valueOf(!allowNulls[i]));
				map.put("remark",remarks[i]);
				map.put("label",getFieldLabel(fields[i],remarks[i]));
				String inputType=getInputType(javaFieldName,fieldTypes[i]);
				if(inputType.equals("textarea"))
					continue;
				map.put("inputType",inputType);
				if("Date,TimeStamp".contains(fieldTypes[i].getSimpleName()))
					continue;
				if(javaFieldName.equals("centerId"))
					continue;
				map.put("dropdownUrl",getDropdownUrl(javaFieldName,fieldTypes[i],fields));
				result.add(map);
			}
		}
		return result;
	}
	
	private boolean hasIdField(String nameField,String[] fields) {
		if(nameField.endsWith("_name")) {
			String idField=nameField.replace("_name","_id");
			return esdk.array.contains(fields,idField);
		}
		else
			return false;
	}
	
	
	private List<Map> getSearchTimeFieldList(Object metadata) {
		String[] fields=(String[])EasyReflect.getDeclaredFieldValue(metadata,"FieldNames");
		Class[] fieldTypes=(Class[])EasyReflect.getDeclaredFieldValue(metadata,"FieldTypes");
		String[] remarks=(String[])EasyReflect.getDeclaredFieldValue(metadata,"Remarks");
		String[] excludeFieldNames= {"create_user_name","modify_user_name","delete_user_name","delete_time","upload_user_name","version"};
		List<Map> result=new ArrayList();
		for(int i=0;i<fields.length;i++) {
			if("Date,TimeStamp".contains(fieldTypes[i].getSimpleName())&&!esdk.str.existOf(excludeFieldNames,fields[i])) {
				String javaFieldName=esdk.str.toCamelCase(fields[i],false);
				result.add(esdk.map.createMap("fieldName",javaFieldName,"label",getFieldLabel(fields[i],remarks[i])));
			}
		}
		return result;
	}

	private String getFieldLabel(String field,String remark){
		String result=esdk.obj.or(remark.toUpperCase(),Constant.CommonColumnMap.get(field),field);
		if(result.contains("(")||result.contains("（"))
			result=esdk.regex.findSub(result,"(.*?)[\\(,，（：]",1);
		if(result.endsWith("ID"))
			result=result.replace("ID","名称");
		return result.trim();
	}

	public String getTemplatePath(){
		return templatePath;
	}

	public void setTemplatePath(String templatePath){
		this.templatePath = templatePath;
	}
	
	public void setGenerateService(boolean isGenerateService){
		this.isGenerateService = isGenerateService;
	}

	public void setGenerateVuePage(boolean isGenerateVuePage){
		this.isGenerateVuePage = isGenerateVuePage;
	}
	
	public void setGenerateUploadService(boolean isGenerateUploadService){
		this.isGenerateUploadService = isGenerateUploadService;
	}

	static public class Max implements TemplateMethodModelEx {
		public Object exec(List arguments) throws TemplateModelException {
			return esdk.math.max(Integer.parseInt(arguments.get(0).toString()),Integer.parseInt(arguments.get(1).toString()))+"";
		}
	}
	
//	public static void main(String[] args) throws Exception{
//		System.out.println("Code generate processing...");
//		TimeMeter tm=new TimeMeter();
//		new ModulesCodeGenerator().generateFiles("case_referral");
//		tm.printElapse("Code generate process is finished. 耗费时间:{}.");
//	}
}
